import os
import re
import dash
import pandas as pd
from flask import request
from dash import html, dcc
import plotly.express as px
import feffery_antd_components as fac
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__)


# 这里的app即为Dash实例
@app.server.route('/upload/', methods=['POST'])
def upload():
    '''
    构建文件上传服务
    :return:
    '''

    # 获取上传id参数，用于指向保存路径
    uploadId = request.values.get('uploadId')

    # 获取上传的文件名称
    filename = request.files['file'].filename

    # 基于上传id，若本地不存在则会自动创建目录
    try:
        os.mkdir(os.path.join('caches', uploadId))
    except FileExistsError:
        pass

    # 流式写出文件到指定目录
    with open(os.path.join('caches', uploadId, filename), 'wb') as f:
        # 流式写出大型文件，这里的10代表10MB
        for chunk in iter(lambda: request.files['file'].read(1024 * 1024 * 10), b''):
            f.write(chunk)

    return {'filename': filename}


@app.callback(
    [Output('value-column', 'options'),
     Output('path-columns', 'options')],
    Input('upload-dataset', 'lastUploadTaskRecord')
)
def generate_select_options(lastUploadTaskRecord):
    if lastUploadTaskRecord:
        df = pd.read_csv(os.path.join('caches', lastUploadTaskRecord['taskId'], lastUploadTaskRecord['fileName']))

        return [
            [
                {
                    'label': column,
                    'value': column
                }
                for column in df.select_dtypes('number').columns
            ],
            [
                {
                    'label': column,
                    'value': column
                }
                for column in df.select_dtypes('object').columns
            ]
        ]

    return [], []


@app.callback(
    Output('treemap', 'children'),
    [Input('value-column', 'value'),
     Input('path-columns', 'value'),
     Input('colormap', 'value'),
     Input('midpoint', 'value')],
    State('upload-dataset', 'lastUploadTaskRecord')
)
def render_treemap(value_column, path_columns, colormap, midpoint, lastUploadTaskRecord):
    if all([value_column, path_columns, colormap, lastUploadTaskRecord]):
        df = pd.read_csv(os.path.join('caches', lastUploadTaskRecord['taskId'], lastUploadTaskRecord['fileName']))

        fig = px.treemap(df, path=[px.Constant("根节点")] + path_columns,
                         values=value_column,
                         color=value_column,
                         color_continuous_scale=colormap,
                         color_continuous_midpoint=midpoint)
        fig.update_layout(margin=dict(t=0, l=0, r=0, b=0))

        return dcc.Graph(
            figure=fig,
            style={
                'height': '100%',
                'width': '100%'
            }
        )

    return fac.AntdEmpty(
        description='请先完善绘图所需各参数！'
    )


app.layout = html.Div(
    html.Div(
        [
            fac.AntdPopover(
                fac.AntdUpload(
                    id='upload-dataset',
                    apiUrl='/upload/',
                    buttonContent='上传我的csv表格数据',
                    fileTypes=['csv'],
                    fileListMaxLength=1
                ),
                title='示例数据下载',
                content=[
                    html.A(
                        '点击下载',
                        href='/assets/demo.csv',
                        target='_blank'
                    )
                ],
                placement='right'
            ),

            fac.AntdSpace(
                [
                    fac.AntdSelect(
                        id='value-column',
                        placeholder='请选择数值映射字段',
                        style={
                            'width': '250px'
                        }
                    ),

                    fac.AntdSelect(
                        id='path-columns',
                        mode='multiple',
                        placeholder='请按顺序选择路径分层对应的多个字段',
                        style={
                            'width': '300px'
                        }
                    ),

                    fac.AntdSelect(
                        id='colormap',
                        placeholder='请选择色彩映射方案',
                        options=[
                            {
                                'label': color,
                                'value': color
                            }
                            for color in dir(px.colors.colorbrewer) if re.findall('^[A-Z]+', color)
                        ],
                        style={
                            'width': '250px'
                        }
                    ),

                    fac.AntdInputNumber(
                        id='midpoint',
                        placeholder='（可选）设置映射范围中点值',
                        style={
                            'width': '225px'
                        }
                    )
                ],
                style={
                    'margin': '15px 0'
                }
            ),

            html.Div(
                id='treemap',
                style={
                    'height': '700px',
                    'display': 'flex',
                    'alignItems': 'center',
                    'justifyContent': 'center'
                }
            )
        ],
        style={
            'height': '100%',
            'borderRadius': '10px',
            'boxShadow': '0 6px 16px rgb(107 147 224 / 14%)',
            'padding': '20px'
        }
    ),
    style={
        'padding': '50px'
    }
)

if __name__ == '__main__':
    app.run_server(debug=True)
